/*-------------------------------------------------------------------------
 *
 * copyfromparse.c
 *		解析 CSV/text/binary 格式以进行 COPY FROM。
 *
 * 此文件包含解析文本、CSV 和二进制输入格式的例程。  主要入口点是 NextCopyFrom()，它解析下一条输入行并将其作为 Datums 返回。
 *
 * 在文本/CSV 模式下，解析分为多个阶段：
 *
 * [数据源] --> raw_buf --> input_buf --> line_buf --> attribute_buf
 *                1.          2.            3.           4.
 *
 * 1. CopyLoadRawBuf() 从输入文件或客户端读取原始数据，并将其放入 'raw_buf'。
 *
 * 2. CopyConvertBuf() 调用编码转换函数，将 'raw_buf' 中的数据从客户端编码转换为服务器编码，将转换结果放入 'input_buf'。
 *
 * 3. CopyReadLine() 解析 'input_buf' 中的数据，一次一行。 它负责找到下一个换行符标记，并根据 COPY 选项考虑引号和转义字符。 行被复制到 'line_buf' 中，带有引号和转义字符。
 *
 * 4. CopyReadAttributesText/CSV() 函数从 'line_buf' 中获取输入行，并将其拆分为字段，按需取消转义数据。 字段存储在 'attribute_buf' 中，'raw_fields' 数组保存每个字段的指针。
 *
 * 如果不需要编码转换，则在第 2 步中可以采取简化方式，避免不必要地复制数据。 'input_buf' 指针直接指向 'raw_buf'，因此 CopyLoadRawBuf() 将原始数据直接加载到 'input_buf' 中。  CopyConvertBuf() 仅验证数据在当前编码下是否有效。
 *
 * 在二进制模式中，管道简单得多。 输入加载到 'raw_buf' 中，编码转换在特定数据类型的接收函数中完成（如果需要）。 'input_buf' 和 'line_buf' 不使用，但 'attribute_buf' 用作临时缓冲区，以保存传递给接收函数的数据集的一个属性的数据。
 *
 * 'raw_buf' 始终为 64 kB 大小 (RAW_BUF_SIZE)。 'input_buf' 也是 64 kB (INPUT_BUF_SIZE)，如果需要进行编码转换。 'line_buf' 和 'attribute_buf' 基于需要扩展，以容纳迄今为止遇到的最长行。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/copyfromparse.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <ctype.h>
#include <unistd.h>
#include <sys/stat.h>

#include "commands/copy.h"
#include "commands/copyfrom_internal.h"
#include "commands/progress.h"
#include "executor/executor.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "port/pg_bswap.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/rel.h"

#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#define OCTVALUE(c) ((c) - '0')

/*
 * 这些宏集中处理 line_buf 和 input_buf 缓冲区的代码。
 * 它们是宏，因为它们通常涉及 continue/break 控制，并且为了避免在紧凑的 COPY 循环中函数调用的开销。
 *
 * 我们必须使用 "if (1)" 因为常规的 "do {...} while(0)" 包装将会
 * 阻止 continue/break 处理正常工作。我们用 "else ((void) 0)" 结束 "if (1)"
 * 以确保 "if" 不会无意匹配到调用代码中的任何 "else"，并避免任何有关
 * 空语句的编译器警告。请参见 http://www.cit.gu.edu.au/~anthony/info/C/C.macros。
 */

/*
 * 即使有多个预读，这也会在循环顶部将读取的字符保存在缓冲区中。
 */
#define IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(extralen) \
if (1) \
{ \
	if (fc_input_buf_ptr + (extralen) >= fc_copy_buf_len && !fc_hit_eof) \
	{ \
		fc_input_buf_ptr = fc_prev_raw_ptr; /* 撤销获取 */ \
		fc_need_data = true; \
		continue; \
	} \
} else ((void) 0)

/* 这将消耗缓冲区的其余部分并中断 */
#define IF_NEED_REFILL_AND_EOF_BREAK(extralen) \
if (1) \
{ \
	if (fc_input_buf_ptr + (extralen) >= fc_copy_buf_len && fc_hit_eof) \
	{ \
		if (extralen) \
			fc_input_buf_ptr = fc_copy_buf_len; /* 消耗部分字符 */ \
		/* 在 EOF 前的反斜杠，视为数据字符 */ \
		fc_result = true; \
		break; \
	} \
} else ((void) 0)

/*
 * 将任何批准的数据传输到 line_buf；必须这样做以确保
 * input_buf 中有足够的空间。
 */
#define REFILL_LINEBUF \
if (1) \
{ \
	if (fc_input_buf_ptr > fc_cstate->input_buf_index) \
	{ \
		appendBinaryStringInfo(&fc_cstate->line_buf, \
							 fc_cstate->input_buf + fc_cstate->input_buf_index, \
							   fc_input_buf_ptr - fc_cstate->input_buf_index); \
		fc_cstate->input_buf_index = fc_input_buf_ptr; \
	} \
} else ((void) 0)

/* 撤销任何预读并跳出块。 */
#define NO_END_OF_COPY_GOTO \
if (1) \
{ \
	fc_input_buf_ptr = fc_prev_raw_ptr + 1; \
	goto not_end_of_copy; \
} else ((void) 0)

/* 注意：在 copyto.c 中有一个副本 */
static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";


/* 非导出函数原型 */
static bool fc_CopyReadLine(CopyFromState fc_cstate);
static bool fc_CopyReadLineText(CopyFromState fc_cstate);
static int	fc_CopyReadAttributesText(CopyFromState fc_cstate);
static int	fc_CopyReadAttributesCSV(CopyFromState fc_cstate);
static Datum fc_CopyReadBinaryAttribute(CopyFromState fc_cstate, FmgrInfo *fc_flinfo,
									 Oid fc_typioparam, int32 fc_typmod,
									 bool *fc_isnull);


/* 低级通信函数 */
static int	fc_CopyGetData(CopyFromState fc_cstate, void *fc_databuf,
						int fc_minread, int fc_maxread);
static inline bool fc_CopyGetInt32(CopyFromState fc_cstate, int32 *fc_val);
static inline bool fc_CopyGetInt16(CopyFromState fc_cstate, int16 *fc_val);
static void fc_CopyLoadInputBuf(CopyFromState fc_cstate);
static int	fc_CopyReadBinaryData(CopyFromState fc_cstate, char *fc_dest, int fc_nbytes);

void ReceiveCopyBegin(CopyFromState fc_cstate)
{
	StringInfoData fc_buf;
	int			fc_natts = list_length(fc_cstate->attnumlist);
	int16		fc_format = (fc_cstate->opts.binary ? 1 : 0);
	int			fc_i;

	pq_beginmessage(&fc_buf, 'G');
	pq_sendbyte(&fc_buf, fc_format);	/* 整体格式 */
	pq_sendint16(&fc_buf, fc_natts);
	for (fc_i = 0; fc_i < fc_natts; fc_i++)
		pq_sendint16(&fc_buf, fc_format); /* 每列格式 */
	pq_endmessage(&fc_buf);
	fc_cstate->copy_src = COPY_FRONTEND;
	fc_cstate->fe_msgbuf = makeStringInfo();
	/* 我们 *必须* 在这里刷新以确保 FE 知道可以发送。 */
	pq_flush();
}

void ReceiveCopyBinaryHeader(CopyFromState fc_cstate)
{
	char		fc_readSig[11];
	int32		fc_tmp;

	/* 签名 */
	if (fc_CopyReadBinaryData(fc_cstate, fc_readSig, 11) != 11 ||
		memcmp(fc_readSig, BinarySignature, 11) != 0)
		ereport(ERROR,
				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
				 errmsg("COPY file signature not recognized")));
	/* 标志字段 */
	if (!fc_CopyGetInt32(fc_cstate, &fc_tmp))
		ereport(ERROR,
				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
				 errmsg("invalid COPY file header (missing flags)")));
	if ((fc_tmp & (1 << 16)) != 0)
		ereport(ERROR,
				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
				 errmsg("invalid COPY file header (WITH OIDS)")));
	fc_tmp &= ~(1 << 16);
	if ((fc_tmp >> 16) != 0)
		ereport(ERROR,
				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
				 errmsg("unrecognized critical flags in COPY file header")));
	/* 头扩展长度 */
	if (!fc_CopyGetInt32(fc_cstate, &fc_tmp) ||
		fc_tmp < 0)
		ereport(ERROR,
				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
				 errmsg("invalid COPY file header (missing length)")));
	/* 跳过扩展头（如果存在） */
	while (fc_tmp-- > 0)
	{
		if (fc_CopyReadBinaryData(fc_cstate, fc_readSig, 1) != 1)
			ereport(ERROR,
					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
					 errmsg("invalid COPY file header (wrong length)")));
	}
}

/*
 * CopyGetData 从源（文件或前端）读取数据
 *
 * 我们尝试从源中读取至少 minread，最多 maxread 字节。
 * 返回实际读取的字节数；如果这小于 minread，则检测到 EOF。
 *
 * 注意：从前端复制时，我们期望每个协议都有适当的 EOF 标记；
 * 如果前端简单地断开连接，我们将引发错误。
 * 在这种情况下，允许 COPY IN 正常完成似乎是不明智的。
 *
 * 注意：这里不适用任何数据转换。
 */
static int fc_CopyGetData(CopyFromState fc_cstate, void *fc_databuf, int fc_minread, int fc_maxread)
{
	int			fc_bytesread = 0;

	switch (fc_cstate->copy_src)
	{
		case COPY_FILE:
			fc_bytesread = fread(fc_databuf, 1, fc_maxread, fc_cstate->copy_file);
			if (ferror(fc_cstate->copy_file))
				ereport(ERROR,
						(errcode_for_file_access(),
						 errmsg("could not read from COPY file: %m")));
			if (fc_bytesread == 0)
				fc_cstate->raw_reached_eof = true;
			break;
		case COPY_FRONTEND:
			while (fc_maxread > 0 && fc_bytesread < fc_minread && !fc_cstate->raw_reached_eof)
			{
				int			fc_avail;

				while (fc_cstate->fe_msgbuf->cursor >= fc_cstate->fe_msgbuf->len)
				{
					/* 尝试接收另一个消息 */
					int			fc_mtype;
					int			fc_maxmsglen;

			readmessage:
					HOLD_CANCEL_INTERRUPTS();
					pq_startmsgread();
					fc_mtype = pq_getbyte();
					if (fc_mtype == EOF)
						ereport(ERROR,
								(errcode(ERRCODE_CONNECTION_FAILURE),
								 errmsg("unexpected EOF on client connection with an open transaction")));
					/* 验证消息类型并设置数据包大小限制 */
					switch (fc_mtype)
					{
						case 'd':	/* CopyData */
							fc_maxmsglen = PQ_LARGE_MESSAGE_LIMIT;
							break;
						case 'c':	/* CopyDone */
						case 'f':	/* CopyFail */
						case 'H':	/* Flush */
						case 'S':	/* Sync */
							fc_maxmsglen = PQ_SMALL_MESSAGE_LIMIT;
							break;
						default:
							ereport(ERROR,
									(errcode(ERRCODE_PROTOCOL_VIOLATION),
									 errmsg("unexpected message type 0x%02X during COPY from stdin",
											fc_mtype)));
							fc_maxmsglen = 0;	/* 保持编译器安静 */
							break;
					}
					/* 现在收集消息主体 */
					if (pq_getmessage(fc_cstate->fe_msgbuf, fc_maxmsglen))
						ereport(ERROR,
								(errcode(ERRCODE_CONNECTION_FAILURE),
								 errmsg("unexpected EOF on client connection with an open transaction")));
					RESUME_CANCEL_INTERRUPTS();
					/* ...并处理它 */
					switch (fc_mtype)
					{
						case 'd':	/* CopyData */
							break;
						case 'c':	/* CopyDone */
							/* COPY IN 正确终止于前端 */
							fc_cstate->raw_reached_eof = true;
							return fc_bytesread;
						case 'f':	/* CopyFail */
							ereport(ERROR,
									(errcode(ERRCODE_QUERY_CANCELED),
									 errmsg("COPY from stdin failed: %s",
											pq_getmsgstring(fc_cstate->fe_msgbuf))));
							break;
						case 'H':	/* Flush */
						case 'S':	/* Sync */

							/*
							 * 为了方便客户端库（如 libpq）
							 * 忽略 Flush/Sync，它们可能会在没有注意到
							 * 它们刚刚发送的命令是 COPY 的情况下发送这些。
							 */
							goto readmessage;
						default:
							Assert(false);	/* NOT REACHED */
					}
				}
				fc_avail = fc_cstate->fe_msgbuf->len - fc_cstate->fe_msgbuf->cursor;
				if (fc_avail > fc_maxread)
					fc_avail = fc_maxread;
				pq_copymsgbytes(fc_cstate->fe_msgbuf, fc_databuf, fc_avail);
				fc_databuf = (void *) ((char *) fc_databuf + fc_avail);
				fc_maxread -= fc_avail;
				fc_bytesread += fc_avail;
			}
			break;
		case COPY_CALLBACK:
			fc_bytesread = fc_cstate->data_source_cb(fc_databuf, fc_minread, fc_maxread);
			break;
	}

	return fc_bytesread;
}


/*
 * 这些函数确实应用了一些数据转换
 */

/*
 * CopyGetInt32 读取以网络字节顺序出现的 int32
 *
 * 如果正常返回 true，EOF 时返回 false
 */
static inline bool fc_CopyGetInt32(CopyFromState fc_cstate, int32 *fc_val)
{
	uint32		fc_buf;

	if (fc_CopyReadBinaryData(fc_cstate, (char *) &fc_buf, sizeof(fc_buf)) != sizeof(fc_buf))
	{
		*fc_val = 0;				/* 抑制编译器警告 */
		return false;
	}
	*fc_val = (int32) pg_ntoh32(fc_buf);
	return true;
}

/*
 * CopyGetInt16 读取以网络字节顺序出现的 int16
 */
static inline bool fc_CopyGetInt16(CopyFromState fc_cstate, int16 *fc_val)
{
	uint16		fc_buf;

	if (fc_CopyReadBinaryData(fc_cstate, (char *) &fc_buf, sizeof(fc_buf)) != sizeof(fc_buf))
	{
		*fc_val = 0;				/* 抑制编译器警告 */
		return false;
	}
	*fc_val = (int16) pg_ntoh16(fc_buf);
	return true;
}


/*
 * 在 'raw_buf' 中执行编码转换，将转换后的数据写入 'input_buf'。
 *
 * 进入时，'raw_buf' 中必须有要转换的数据。
 */
static void fc_CopyConvertBuf(CopyFromState fc_cstate)
{
	/*
	 * 如果文件和服务器的编码相同，则不需要进行编码转换。
	 * 但是，我们仍然需要验证输入是否对编码有效。
	 */
	if (!fc_cstate->need_transcoding)
	{
		/*
		 * 当不需要转换时，input_buf 和 raw_buf 是相同的。
		 * raw_buf_len 是缓冲区中字节的总数，而 input_buf_len 跟踪
		 * 已经验证过的字节数。
		 */
		int			fc_preverifiedlen = fc_cstate->input_buf_len;
		int			fc_unverifiedlen = fc_cstate->raw_buf_len - fc_cstate->input_buf_len;
		int			fc_nverified;

		if (fc_unverifiedlen == 0)
		{
			/*
			 * 如果不再有原始数据输入，向调用者报告 EOF。
			 */
			if (fc_cstate->raw_reached_eof)
				fc_cstate->input_reached_eof = true;
			return;
		}

		/*
		 * 验证新数据，包括来自之前回合的任何剩余未验证字节。
		 */
		fc_nverified = pg_encoding_verifymbstr(fc_cstate->file_encoding,
											fc_cstate->raw_buf + fc_preverifiedlen,
											fc_unverifiedlen);
		if (fc_nverified == 0)
		{
			/*
			 * 无法验证任何内容。
			 *
			 * 如果不再有原始输入数据，意味着最后有一个不完整的多字节序列。
			 * 此外，如果还有“足够”的输入剩余，我们应该能够验证
			 * 至少一个字符，无法验证则意味着我们碰到了无效的字节序列。
			 */
			if (fc_cstate->raw_reached_eof || fc_unverifiedlen >= pg_encoding_max_length(fc_cstate->file_encoding))
				fc_cstate->input_reached_error = true;
			return;
		}
		fc_cstate->input_buf_len += fc_nverified;
	}
	else
	{
		/*
		 * 需要进行编码转换。
		 */
		int			fc_nbytes;
		unsigned char *fc_src;
		int			fc_srclen;
		unsigned char *fc_dst;
		int			fc_dstlen;
		int			fc_convertedlen;

		if (RAW_BUF_BYTES(fc_cstate) == 0)
		{
			/*
			 * 如果不再有原始数据输入，向调用者报告 EOF。
			 */
			if (fc_cstate->raw_reached_eof)
				fc_cstate->input_reached_eof = true;
			return;
		}

		/*
		 * 首先，复制任何未处理的数据。
		 */
		fc_nbytes = INPUT_BUF_BYTES(fc_cstate);
		if (fc_nbytes > 0 && fc_cstate->input_buf_index > 0)
			memmove(fc_cstate->input_buf, fc_cstate->input_buf + fc_cstate->input_buf_index,
					fc_nbytes);
		fc_cstate->input_buf_index = 0;
		fc_cstate->input_buf_len = fc_nbytes;
		fc_cstate->input_buf[fc_nbytes] = '\0';

		fc_src = (unsigned char *) fc_cstate->raw_buf + fc_cstate->raw_buf_index;
		fc_srclen = fc_cstate->raw_buf_len - fc_cstate->raw_buf_index;
		fc_dst = (unsigned char *) fc_cstate->input_buf + fc_cstate->input_buf_len;
		fc_dstlen = INPUT_BUF_SIZE - fc_cstate->input_buf_len + 1;

		/*
		 * 执行转换。如果输入中有无效的字节序列，这可能会提前停止。
		 * 在这种情况下，我们将尽可能多地进行转换。
		 *
		 * 注意：即使我们碰到了无效的字节序列，也不会报告错误，
		 * 直到所有有效字节都被消耗。输入可能包含一个输入结束标记（\.），
		 * 如果无效字节序列在输入结束标记之后，我们不希望报告错误。
		 * 只要这些数据在编码中是有效的，我们可能会不必要地转换一些
		 * 输入结束标记之后的数据，但这并无害。
		 */
		fc_convertedlen = pg_do_encoding_conversion_buf(fc_cstate->conversion_proc,
													 fc_cstate->file_encoding,
													 GetDatabaseEncoding(),
													 fc_src, fc_srclen,
													 fc_dst, fc_dstlen,
													 true);
		if (fc_convertedlen == 0)
		{
			/*
			 * 无法转换任何内容。如果不再有原始输入数据，
			 * 意味着最后有一个不完整的多字节序列。
			 * 此外，如果还有大量输入剩余，我们应该能够转换至少一个字符，
			 * 所以无法转换必须意味着我们碰到了无效的字节序列。
			 */
			if (fc_cstate->raw_reached_eof || fc_srclen >= MAX_CONVERSION_INPUT_LENGTH)
				fc_cstate->input_reached_error = true;
			return;
		}
		fc_cstate->raw_buf_index += fc_convertedlen;
		fc_cstate->input_buf_len += strlen((char *) fc_dst);
	}
}

/*
 * 报告编码或转换错误。
 */
static void fc_CopyConversionError(CopyFromState fc_cstate)
{
	Assert(fc_cstate->raw_buf_len > 0);
	Assert(fc_cstate->input_reached_error);

	if (!fc_cstate->need_transcoding)
	{
		/*
		 * 一直到 input_buf_len 的所有内容都已成功验证，
		 * input_buf_len 指向无效或不完整的字符。
		 */
		report_invalid_encoding(fc_cstate->file_encoding,
								fc_cstate->raw_buf + fc_cstate->input_buf_len,
								fc_cstate->raw_buf_len - fc_cstate->input_buf_len);
	}
	else
	{
		/*
		 * raw_buf_index 指向无效或无法转换的字符。
		 * 我们让转换例程报告错误，因为它可以提供比我们在这里
		 * 能提供的更具体的错误信息。CopyConvertBuf() 中
		 * 对转换例程的早期调用检测到错误，现在我们以 noError=false
		 * 再次调用转换例程，以让它抛出错误。
		 */
		unsigned char *fc_src;
		int			fc_srclen;
		unsigned char *fc_dst;
		int			fc_dstlen;

		fc_src = (unsigned char *) fc_cstate->raw_buf + fc_cstate->raw_buf_index;
		fc_srclen = fc_cstate->raw_buf_len - fc_cstate->raw_buf_index;
		fc_dst = (unsigned char *) fc_cstate->input_buf + fc_cstate->input_buf_len;
		fc_dstlen = INPUT_BUF_SIZE - fc_cstate->input_buf_len + 1;

		(void) pg_do_encoding_conversion_buf(fc_cstate->conversion_proc,
											 fc_cstate->file_encoding,
											 GetDatabaseEncoding(),
											 fc_src, fc_srclen,
											 fc_dst, fc_dstlen,
											 false);

		/*
		 * 转换例程应该报告一个错误，所以不应该到达这里。
		 */
		elog(ERROR, "encoding conversion failed without error");
	}
}

/*
 * 从数据源加载更多数据到 raw_buf。
 *
 * 如果 RAW_BUF_BYTES(cstate) > 0，未处理的字节将移动到缓冲区的
 * 开始位置，然后我们在其后加载新数据。
 */
static void fc_CopyLoadRawBuf(CopyFromState fc_cstate)
{
	int			fc_nbytes;
	int			fc_inbytes;

	/*
	 * 在文本模式下，如果不需要进行编码转换，raw_buf 和
	 * input_buf 指向同一个缓冲区。它们的 len/index 也应该一致。
	 */
	if (fc_cstate->raw_buf == fc_cstate->input_buf)
	{
		Assert(!fc_cstate->need_transcoding);
		Assert(fc_cstate->raw_buf_index == fc_cstate->input_buf_index);
		Assert(fc_cstate->input_buf_len <= fc_cstate->raw_buf_len);
	}

	/*
	 * 如果有未处理的数据，则复制这些数据。
	 */
	fc_nbytes = RAW_BUF_BYTES(fc_cstate);
	if (fc_nbytes > 0 && fc_cstate->raw_buf_index > 0)
		memmove(fc_cstate->raw_buf, fc_cstate->raw_buf + fc_cstate->raw_buf_index,
				fc_nbytes);
	fc_cstate->raw_buf_len -= fc_cstate->raw_buf_index;
	fc_cstate->raw_buf_index = 0;

	/*
	 * 如果 raw_buf 和 input_buf 实际上是同一个缓冲区，也要调整
	 * input_buf 变量。
	 */
	if (fc_cstate->raw_buf == fc_cstate->input_buf)
	{
		fc_cstate->input_buf_len -= fc_cstate->input_buf_index;
		fc_cstate->input_buf_index = 0;
	}

	/* 加载更多数据 */
	fc_inbytes = fc_CopyGetData(fc_cstate, fc_cstate->raw_buf + fc_cstate->raw_buf_len,
						  1, RAW_BUF_SIZE - fc_cstate->raw_buf_len);
	fc_nbytes += fc_inbytes;
	fc_cstate->raw_buf[fc_nbytes] = '\0';
	fc_cstate->raw_buf_len = fc_nbytes;

	fc_cstate->bytes_processed += fc_inbytes;
	pgstat_progress_update_param(PROGRESS_COPY_BYTES_PROCESSED, fc_cstate->bytes_processed);

	if (fc_inbytes == 0)
		fc_cstate->raw_reached_eof = true;
}

/*
 * CopyLoadInputBuf 加载更多数据到 input_buf
 *
 * 返回时，至少会再将一个输入字符加载到
 * input_buf 中，或者设置 input_reached_eof。
 *
 * 如果 INPUT_BUF_BYTES(cstate) > 0，未处理的字节会移动到缓冲区的开始位置，
 * 然后我们在其后加载更多数据。
 */
static void fc_CopyLoadInputBuf(CopyFromState fc_cstate)
{
	int			fc_nbytes = INPUT_BUF_BYTES(fc_cstate);

	/*
	 * 调用者已经更新了 input_buf_index，以指示输入已经消耗了多少，
	 * 不再需要。如果 input_buf 与 raw_buf 是同一个物理区域，
	 * 则相应地更新 raw_buf_index。
	 */
	if (fc_cstate->raw_buf == fc_cstate->input_buf)
	{
		Assert(!fc_cstate->need_transcoding);
		Assert(fc_cstate->input_buf_index >= fc_cstate->raw_buf_index);
		fc_cstate->raw_buf_index = fc_cstate->input_buf_index;
	}

	for (;;)
	{
		/* 如果现在有一些未转换的数据，尝试将其转换 */
		fc_CopyConvertBuf(fc_cstate);

		/* 如果现在有更多的输入字节准备好了，返回这些字节 */
		if (INPUT_BUF_BYTES(fc_cstate) > fc_nbytes)
			return;

		/*
		 * 如果我们达到了一个无效的字节序列，或者我们处于一个不完整
		 * 的多字节字符，但没有更多的原始输入数据，报告转换错误。
		 */
		if (fc_cstate->input_reached_error)
			fc_CopyConversionError(fc_cstate);

		/* 没有更多输入，所有数据都已转换 */
		if (fc_cstate->input_reached_eof)
			break;

		/* 尝试加载更多原始数据 */
		Assert(!fc_cstate->raw_reached_eof);
		fc_CopyLoadRawBuf(fc_cstate);
	}
}

/*
 * CopyReadBinaryData
 *
 * 从 cstate->copy_file 通过 cstate->raw_buf 读取最多 'nbytes' 字节
 * 并将其写入 'dest'。返回读取的字节数（如果达到 EOF，则少于 'nbytes'）。
 */
static int fc_CopyReadBinaryData(CopyFromState fc_cstate, char *fc_dest, int fc_nbytes)
{
	int			fc_copied_bytes = 0;

	if (RAW_BUF_BYTES(fc_cstate) >= fc_nbytes)
	{
		/* 缓冲区中有足够的字节。 */
		memcpy(fc_dest, fc_cstate->raw_buf + fc_cstate->raw_buf_index, fc_nbytes);
		fc_cstate->raw_buf_index += fc_nbytes;
		fc_copied_bytes = fc_nbytes;
	}
	else
	{
		/*
		 * 缓冲区中的字节不够，因此必须从文件中读取。需要
		 * 循环，因为 'nbytes' 可能大于缓冲区大小。
		 */
		do
		{
			int			fc_copy_bytes;

			/* 如果缓冲区为空，加载更多数据。 */
			if (RAW_BUF_BYTES(fc_cstate) == 0)
			{
				fc_CopyLoadRawBuf(fc_cstate);
				if (fc_cstate->raw_reached_eof)
					break;		/* EOF */
			}

			/* 传输一些字节。 */
			fc_copy_bytes = Min(fc_nbytes - fc_copied_bytes, RAW_BUF_BYTES(fc_cstate));
			memcpy(fc_dest, fc_cstate->raw_buf + fc_cstate->raw_buf_index, fc_copy_bytes);
			fc_cstate->raw_buf_index += fc_copy_bytes;
			fc_dest += fc_copy_bytes;
			fc_copied_bytes += fc_copy_bytes;
		} while (fc_copied_bytes < fc_nbytes);
	}

	return fc_copied_bytes;
}

/*
 * 以文本或 csv 模式读取下一行中的原始字段，进行COPY FROM。
 * 如果没有更多行，返回 false。
 *
 * 通过 'fields' 返回一个内部临时缓冲区。在下一次调用函数之前有效。
 * 由于函数返回输入文件中的所有原始字段，'nfields'
 * 可能与关系中的列数不同。
 *
 * 注意：force_not_null 选项不适用于返回的字段。
 */
bool NextCopyFromRawFields(CopyFromState fc_cstate, char ***fc_fields, int *fc_nfields)
{
	int			fc_fldct;
	bool		fc_done;

	/* 仅适用于文本或 csv 输入 */
	Assert(!fc_cstate->opts.binary);

	/* 在输入时检查标题行是否正确（如有必要） */
	if (fc_cstate->cur_lineno == 0 && fc_cstate->opts.header_line)
	{
		ListCell   *fc_cur;
		TupleDesc	fc_tupDesc;

		fc_tupDesc = RelationGetDescr(fc_cstate->rel);

		fc_cstate->cur_lineno++;
		fc_done = fc_CopyReadLine(fc_cstate);

		if (fc_cstate->opts.header_line == COPY_HEADER_MATCH)
		{
			int			fc_fldnum;

			if (fc_cstate->opts.csv_mode)
				fc_fldct = fc_CopyReadAttributesCSV(fc_cstate);
			else
				fc_fldct = fc_CopyReadAttributesText(fc_cstate);

			if (fc_fldct != list_length(fc_cstate->attnumlist))
				ereport(ERROR,
						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
						 errmsg("wrong number of fields in header line: got %d, expected %d",
								fc_fldct, list_length(fc_cstate->attnumlist))));

			fc_fldnum = 0;
			foreach(fc_cur, fc_cstate->attnumlist)
			{
				int			fc_attnum = lfirst_int(fc_cur);
				char	   *fc_colName;
				Form_pg_attribute fc_attr = TupleDescAttr(fc_tupDesc, fc_attnum - 1);

				Assert(fc_fldnum < fc_cstate->max_fields);

				fc_colName = fc_cstate->raw_fields[fc_fldnum++];
				if (fc_colName == NULL)
					ereport(ERROR,
							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
							 errmsg("column name mismatch in header line field %d: got null value (\"%s\"), expected \"%s\"",
									fc_fldnum, fc_cstate->opts.null_print, NameStr(fc_attr->attname))));

				if (namestrcmp(&fc_attr->attname, fc_colName) != 0)
				{
					ereport(ERROR,
							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
							 errmsg("column name mismatch in header line field %d: got \"%s\", expected \"%s\"",
									fc_fldnum, fc_colName, NameStr(fc_attr->attname))));
				}
			}
		}

		if (fc_done)
			return false;
	}

	fc_cstate->cur_lineno++;

	/* 实际将行读取到内存中 */
	fc_done = fc_CopyReadLine(fc_cstate);

	/*
	 * 行首的 EOF 意味着我们完成了。如果我们在某些字符后看到 EOF，
	 * 我们将视为它是换行符后跟 EOF，即，处理该行，然后在下一个迭代中退出循环。
	 */
	if (fc_done && fc_cstate->line_buf.len == 0)
		return false;

	/* 将行解析为取消转义的字段值 */
	if (fc_cstate->opts.csv_mode)
		fc_fldct = fc_CopyReadAttributesCSV(fc_cstate);
	else
		fc_fldct = fc_CopyReadAttributesText(fc_cstate);

	*fc_fields = fc_cstate->raw_fields;
	*fc_nfields = fc_fldct;
	return true;
}

/*
 * 从文件中读取下一个元组以进行COPY FROM。如果没有更多元组则返回false。
 *
 * 'econtext'用于评估未从文件读取的每列的默认表达式。当未使用默认值时，即
 * 所有列均从文件读取时，它可以为NULL。
 *
 * 'values'和'nulls'数组的长度必须与传递给BeginCopyFrom的关系的列数相同。
 * 此函数填充这些数组。
 */
bool NextCopyFrom(CopyFromState fc_cstate, ExprContext *fc_econtext,
			 Datum *fc_values, bool *fc_nulls)
{
	TupleDesc	fc_tupDesc;
	AttrNumber	fc_num_phys_attrs,
				fc_attr_count,
				fc_num_defaults = fc_cstate->num_defaults;
	FmgrInfo   *fc_in_functions = fc_cstate->in_functions;
	Oid		   *fc_typioparams = fc_cstate->typioparams;
	int			fc_i;
	int		   *fc_defmap = fc_cstate->defmap;
	ExprState **fc_defexprs = fc_cstate->defexprs;

	fc_tupDesc = RelationGetDescr(fc_cstate->rel);
	fc_num_phys_attrs = fc_tupDesc->natts;
	fc_attr_count = list_length(fc_cstate->attnumlist);

	/* 为行的所有值初始化为NULL */
	MemSet(fc_values, 0, fc_num_phys_attrs * sizeof(Datum));
	MemSet(fc_nulls, true, fc_num_phys_attrs * sizeof(bool));

	if (!fc_cstate->opts.binary)
	{
		char	  **fc_field_strings;
		ListCell   *fc_cur;
		int			fc_fldct;
		int			fc_fieldno;
		char	   *fc_string;

		/* 读取下一行中的原始字段 */
		if (!NextCopyFromRawFields(fc_cstate, &fc_field_strings, &fc_fldct))
			return false;

		/* 检查字段溢出 */
		if (fc_attr_count > 0 && fc_fldct > fc_attr_count)
			ereport(ERROR,
					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
					 errmsg("extra data after last expected column")));

		fc_fieldno = 0;

		/* 循环读取行上的用户属性。 */
		foreach(fc_cur, fc_cstate->attnumlist)
		{
			int			fc_attnum = lfirst_int(fc_cur);
			int			fc_m = fc_attnum - 1;
			Form_pg_attribute fc_att = TupleDescAttr(fc_tupDesc, fc_m);

			if (fc_fieldno >= fc_fldct)
				ereport(ERROR,
						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
						 errmsg("missing data for column \"%s\"",
								NameStr(fc_att->attname))));
			fc_string = fc_field_strings[fc_fieldno++];

			if (fc_cstate->convert_select_flags &&
				!fc_cstate->convert_select_flags[fc_m])
			{
				/* 忽略输入字段，保留列为NULL */
				continue;
			}

			if (fc_cstate->opts.csv_mode)
			{
				if (fc_string == NULL &&
					fc_cstate->opts.force_notnull_flags[fc_m])
				{
					/*
					 * 设置了FORCE_NOT_NULL选项且列为NULL -
					 * 将其转换为NULL字符串。
					 */
					fc_string = fc_cstate->opts.null_print;
				}
				else if (fc_string != NULL && fc_cstate->opts.force_null_flags[fc_m]
						 && strcmp(fc_string, fc_cstate->opts.null_print) == 0)
				{
					/*
					 * 设置了FORCE_NULL选项且列匹配NULL
					 * 字符串。它必须被引号引起来，否则该
					 * 字符串将已被设置为NULL。根据规定将其
					 * 转换为NULL。
					 */
					fc_string = NULL;
				}
			}

			fc_cstate->cur_attname = NameStr(fc_att->attname);
			fc_cstate->cur_attval = fc_string;
			fc_values[fc_m] = InputFunctionCall(&fc_in_functions[fc_m],
										  fc_string,
										  fc_typioparams[fc_m],
										  fc_att->atttypmod);
			if (fc_string != NULL)
				fc_nulls[fc_m] = false;
			fc_cstate->cur_attname = NULL;
			fc_cstate->cur_attval = NULL;
		}

		Assert(fc_fieldno == fc_attr_count);
	}
	else
	{
		/* 二进制 */
		int16		fc_fld_count;
		ListCell   *fc_cur;

		fc_cstate->cur_lineno++;

		if (!fc_CopyGetInt16(fc_cstate, &fc_fld_count))
		{
			/* 检测到EOF（文件结束，或协议级EOF） */
			return false;
		}

		if (fc_fld_count == -1)
		{
			/*
			 * 收到EOF标记。等待协议级EOF，如果没有立即到达则抱怨。
			 * 在COPY FROM STDIN中，这确保我们正确处理CopyFail，如果
			 * 客户端选择现在发送该消息。当从文件复制时，我们可以
			 * 像文本模式一样忽略文件的其余部分，但我们选择
			 * 与COPY FROM STDIN情况保持一致。
			 */
			char		fc_dummy;

			if (fc_CopyReadBinaryData(fc_cstate, &fc_dummy, 1) > 0)
				ereport(ERROR,
						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
						 errmsg("received copy data after EOF marker")));
			return false;
		}

		if (fc_fld_count != fc_attr_count)
			ereport(ERROR,
					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
					 errmsg("row field count is %d, expected %d",
							(int) fc_fld_count, fc_attr_count)));

		foreach(fc_cur, fc_cstate->attnumlist)
		{
			int			fc_attnum = lfirst_int(fc_cur);
			int			fc_m = fc_attnum - 1;
			Form_pg_attribute fc_att = TupleDescAttr(fc_tupDesc, fc_m);

			fc_cstate->cur_attname = NameStr(fc_att->attname);
			fc_values[fc_m] = fc_CopyReadBinaryAttribute(fc_cstate,
												&fc_in_functions[fc_m],
												fc_typioparams[fc_m],
												fc_att->atttypmod,
												&fc_nulls[fc_m]);
			fc_cstate->cur_attname = NULL;
		}
	}

	/*
	 * 现在计算并插入未由输入数据提供的列的任何可用默认值。
	 * 在此处或以上未处理的任何内容将保持为NULL。
	 */
	for (fc_i = 0; fc_i < fc_num_defaults; fc_i++)
	{
		/*
		 * 调用者必须提供econtext并切换到其中的每个元组内存上下文。
		 */
		Assert(fc_econtext != NULL);
		Assert(CurrentMemoryContext == fc_econtext->ecxt_per_tuple_memory);

		fc_values[fc_defmap[fc_i]] = ExecEvalExpr(fc_defexprs[fc_i], fc_econtext,
										 &fc_nulls[fc_defmap[fc_i]]);
	}

	return true;
}

/*
 * 读取下一输入行并将其存储在line_buf中。
 *
 * 如果读取到EOF，则结果为true；如果以换行符终止，则为false。
 * 终止的换行符或EOF标记不包括在line_buf的最终值中。
 */
static bool fc_CopyReadLine(CopyFromState fc_cstate)
{
	bool		fc_result;

	resetStringInfo(&fc_cstate->line_buf);
	fc_cstate->line_buf_valid = false;

	/* 解析数据并转移到line_buf中 */
	fc_result = fc_CopyReadLineText(fc_cstate);

	if (fc_result)
	{
		/*
		 * 到达EOF。在协议版本3中，我们应该忽略\以后的任何内容。
		 * 直到协议结束的复制数据。（XXX也许最好不要将\作为特殊处理？）
		 */
		if (fc_cstate->copy_src == COPY_FRONTEND)
		{
			int			fc_inbytes;

			do
			{
				fc_inbytes = fc_CopyGetData(fc_cstate, fc_cstate->input_buf,
									  1, INPUT_BUF_SIZE);
			} while (fc_inbytes > 0);
			fc_cstate->input_buf_index = 0;
			fc_cstate->input_buf_len = 0;
			fc_cstate->raw_buf_index = 0;
			fc_cstate->raw_buf_len = 0;
		}
	}
	else
	{
		/*
		 * 如果没有到达EOF，那么我们一定将EOL标记
		 * 与数据一起传输到line_buf。将其去除。
		 */
		switch (fc_cstate->eol_type)
		{
			case EOL_NL:
				Assert(fc_cstate->line_buf.len >= 1);
				Assert(fc_cstate->line_buf.data[fc_cstate->line_buf.len - 1] == '\n');
				fc_cstate->line_buf.len--;
				fc_cstate->line_buf.data[fc_cstate->line_buf.len] = '\0';
				break;
			case EOL_CR:
				Assert(fc_cstate->line_buf.len >= 1);
				Assert(fc_cstate->line_buf.data[fc_cstate->line_buf.len - 1] == '\r');
				fc_cstate->line_buf.len--;
				fc_cstate->line_buf.data[fc_cstate->line_buf.len] = '\0';
				break;
			case EOL_CRNL:
				Assert(fc_cstate->line_buf.len >= 2);
				Assert(fc_cstate->line_buf.data[fc_cstate->line_buf.len - 2] == '\r');
				Assert(fc_cstate->line_buf.data[fc_cstate->line_buf.len - 1] == '\n');
				fc_cstate->line_buf.len -= 2;
				fc_cstate->line_buf.data[fc_cstate->line_buf.len] = '\0';
				break;
			case EOL_UNKNOWN:
				/* 不应该到这里 */
				Assert(false);
				break;
		}
	}

	/* 现在在错误消息中使用缓冲区是安全的 */
	fc_cstate->line_buf_valid = true;

	return fc_result;
}

/*
 * CopyReadLineText - 文本模式下CopyReadLine的内部循环
 */
static bool fc_CopyReadLineText(CopyFromState fc_cstate)
{
	char	   *fc_copy_input_buf;
	int			fc_input_buf_ptr;
	int			fc_copy_buf_len;
	bool		fc_need_data = false;
	bool		fc_hit_eof = false;
	bool		fc_result = false;

	/* CSV变量 */
	bool		fc_first_char_in_line = true;
	bool		fc_in_quote = false,
				fc_last_was_esc = false;
	char		fc_quotec = '\0';
	char		fc_escapec = '\0';

	if (fc_cstate->opts.csv_mode)
	{
		fc_quotec = fc_cstate->opts.quote[0];
		fc_escapec = fc_cstate->opts.escape[0];
		/* 如果与quotec相同，则忽略特殊转义处理 */
		if (fc_quotec == fc_escapec)
			fc_escapec = '\0';
	}

	/*
	 * 循环的目标是将整个下一输入行转移到 line_buf 中。因此，我们只关心检测换行符（\r 和/或 \n）以及结束复制标记（\.）。
	 *
	 * 在 CSV 模式下，引用字段内部的 \r 和 \n 只是数据值的一部分，并被放入 line_buf 中。我们只保留足够的状态以知道我们当前是否在引用字段内。
	 *
	 * 这四个字符，以及 CSV 转义和引号字符，在前端和后端编码中假定是相同的。
	 *
	 * 输入已经被转换为数据库编码。所有支持的服务器编码都有一个特性，即多字节序列中的所有字节都设置了高位，因此多字节字符不能在多字节序列中嵌入任何换行符或转义字符。因此，我们可以逐字节处理输入，而不管编码如何。
	 *
	 * 为了加快速度，我们尝试将数据从 input_buf 移动到 line_buf 中的块，而不是一次一个字符。input_buf_ptr 指向下一个要检查的字符；从 input_buf_index 到 input_buf_ptr 的任何字符都已确定是行的一部分，但尚未转移到 line_buf。
	 *
	 * 为了在循环内再加一点速度，我们将 input_buf 和 input_buf_len 复制到本地变量中。
	 */
	fc_copy_input_buf = fc_cstate->input_buf;
	fc_input_buf_ptr = fc_cstate->input_buf_index;
	fc_copy_buf_len = fc_cstate->input_buf_len;

	for (;;)
	{
		int			fc_prev_raw_ptr;
		char		fc_c;

		/*
		 * 如果需要则加载更多数据。
		 *
		 * TODO：我们可以强制读取四个字节的提前，避免多次调用 IF_NEED_REFILL_AND_NOT_EOF_CONTINUE()。这在旧的 v2 COPY 协议中不安全，但我们不再支持它。
		 */
		if (fc_input_buf_ptr >= fc_copy_buf_len || fc_need_data)
		{
			REFILL_LINEBUF;

			fc_CopyLoadInputBuf(fc_cstate);
			/* 更新我们的本地变量 */
			fc_hit_eof = fc_cstate->input_reached_eof;
			fc_input_buf_ptr = fc_cstate->input_buf_index;
			fc_copy_buf_len = fc_cstate->input_buf_len;

			/*
			 * 如果我们完全没有数据，则退出循环，并报告 EOF。
			 */
			if (INPUT_BUF_BYTES(fc_cstate) <= 0)
			{
				fc_result = true;
				break;
			}
			fc_need_data = false;
		}

		/* 可以提取一个字符 */
		fc_prev_raw_ptr = fc_input_buf_ptr;
		fc_c = fc_copy_input_buf[fc_input_buf_ptr++];

		if (fc_cstate->opts.csv_mode)
		{
			/*
			 * 如果字符是 '\\' 或 '\r'，我们可能需要在下面预读。如果我们还没有下一个字符，就强制获取下一个字符。
			 * 在更改 CSV 状态之前，我们需要这样做，以防其中一个字符也是引号或转义字符。
			 */
			if (fc_c == '\\' || fc_c == '\r')
			{
				IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
			}

			/*
			 * 在这里处理引号和转义字符是有点棘手的。如果引号字符也是转义字符，那就没有问题 - 我们只需将字符用作切换。如果它们不同，我们需要确保只考虑引号字段内部的转义，并且必须紧接在引号字符之前，而不是转义序列中的第二个转义。
			 */
			if (fc_in_quote && fc_c == fc_escapec)
				fc_last_was_esc = !fc_last_was_esc;
			if (fc_c == fc_quotec && !fc_last_was_esc)
				fc_in_quote = !fc_in_quote;
			if (fc_c != fc_escapec)
				fc_last_was_esc = false;

			/*
			 * 更新嵌入的 CR 和/或 LF 字符的行计数必然是有些脆弱的 - 这个测试可能是我们能做的最好的了。（ XXX 是否争论我们应该根本不这样做 --- cur_lineno 是物理计数还是逻辑计数？）
			 */
			if (fc_in_quote && fc_c == (fc_cstate->eol_type == EOL_NL ? '\n' : '\r'))
				fc_cstate->cur_lineno++;
		}

		/* 处理 \r */
		if (fc_c == '\r' && (!fc_cstate->opts.csv_mode || !fc_in_quote))
		{
			/* 检查第一行上的 \r\n，并处理 \r\n。 */
			if (fc_cstate->eol_type == EOL_UNKNOWN ||
				fc_cstate->eol_type == EOL_CRNL)
			{
				/*
				 * 如果需要更多数据，返回循环顶部以加载它。
				 *
				 * 注意，如果我们处于EOF，c将变成'\0'，因为
				 * input_buf的填充是保证的。
				 */
				IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);

				/* 获取下一个字符 */
				fc_c = fc_copy_input_buf[fc_input_buf_ptr];

				if (fc_c == '\n')
				{
					fc_input_buf_ptr++;	/* 吃掉换行符 */
					fc_cstate->eol_type = EOL_CRNL;	/* 以防尚未设置 */
				}
				else
				{
					/* 找到\r，但没有\n */
					if (fc_cstate->eol_type == EOL_CRNL)
						ereport(ERROR,
								(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
								 !fc_cstate->opts.csv_mode ?
								 errmsg("literal carriage return found in data") :
								 errmsg("unquoted carriage return found in data"),
								 !fc_cstate->opts.csv_mode ?
								 errhint("Use \"\\r\" to represent carriage return.") :
								 errhint("Use quoted CSV field to represent carriage return.")));

					/*
					 * 如果我们到这里，说明是第一行，且没有找到
					 * \n，所以不要消耗预览的字符
					 */
					fc_cstate->eol_type = EOL_CR;
				}
			}
			else if (fc_cstate->eol_type == EOL_NL)
				ereport(ERROR,
						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
						 !fc_cstate->opts.csv_mode ?
						 errmsg("literal carriage return found in data") :
						 errmsg("unquoted carriage return found in data"),
						 !fc_cstate->opts.csv_mode ?
						 errhint("Use \"\\r\" to represent carriage return.") :
						 errhint("Use quoted CSV field to represent carriage return.")));
			/* 如果到这里，我们找到了行结束符 */
			break;
		}

		/* 处理\n */
		if (fc_c == '\n' && (!fc_cstate->opts.csv_mode || !fc_in_quote))
		{
			if (fc_cstate->eol_type == EOL_CR || fc_cstate->eol_type == EOL_CRNL)
				ereport(ERROR,
						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
						 !fc_cstate->opts.csv_mode ?
						 errmsg("literal newline found in data") :
						 errmsg("unquoted newline found in data"),
						 !fc_cstate->opts.csv_mode ?
						 errhint("Use \"\\n\" to represent newline.") :
						 errhint("Use quoted CSV field to represent newline.")));
			fc_cstate->eol_type = EOL_NL;	/* 以防尚未设置 */
			/* 如果到这里，我们找到了行结束符 */
			break;
		}

		/*
		 * 在CSV模式中，我们只识别独立一行的\.。这是因为
		 * \.是一个有效的CSV数据值。
		 */
		if (fc_c == '\\' && (!fc_cstate->opts.csv_mode || fc_first_char_in_line))
		{
			char		fc_c2;

			IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
			IF_NEED_REFILL_AND_EOF_BREAK(0);

			/* -----
			 * 获取下一个字符
			 * 注意：我们不改变c，因此如果它不是\.，我们可以继续处理。
			 * -----
			 */
			fc_c2 = fc_copy_input_buf[fc_input_buf_ptr];

			if (fc_c2 == '.')
			{
				fc_input_buf_ptr++;	/* 消耗掉'.' */

				/*
				 * 注意：如果我们在这里循环回去获取更多数据，
				 * 那么CSV状态变化检查重复执行也没关系；我们
				 * 将在这里返回，没有重要状态发生变化。
				 */
				if (fc_cstate->eol_type == EOL_CRNL)
				{
					/* 获取下一个字符 */
					IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
					/* 如果hit_eof，c2将变成'\0' */
					fc_c2 = fc_copy_input_buf[fc_input_buf_ptr++];

					if (fc_c2 == '\n')
					{
						if (!fc_cstate->opts.csv_mode)
							ereport(ERROR,
									(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
									 errmsg("end-of-copy marker does not match previous newline style")));
						else
							NO_END_OF_COPY_GOTO;
					}
					else if (fc_c2 != '\r')
					{
						if (!fc_cstate->opts.csv_mode)
							ereport(ERROR,
									(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
									 errmsg("end-of-copy marker corrupt")));
						else
							NO_END_OF_COPY_GOTO;
					}
				}

				/* 获取下一个字符 */
				IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
				/* 如果hit_eof，c2将变成'\0' */
				fc_c2 = fc_copy_input_buf[fc_input_buf_ptr++];

				if (fc_c2 != '\r' && fc_c2 != '\n')
				{
					if (!fc_cstate->opts.csv_mode)
						ereport(ERROR,
								(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
								 errmsg("end-of-copy marker corrupt")));
					else
						NO_END_OF_COPY_GOTO;
				}

				if ((fc_cstate->eol_type == EOL_NL && fc_c2 != '\n') ||
					(fc_cstate->eol_type == EOL_CRNL && fc_c2 != '\n') ||
					(fc_cstate->eol_type == EOL_CR && fc_c2 != '\r'))
				{
					ereport(ERROR,
							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
							 errmsg("end-of-copy marker does not match previous newline style")));
				}

				/*
				 * 仅将\前的数据传输到line_buf中，然后
				 * 丢弃数据和\.序列。
				 */
				if (fc_prev_raw_ptr > fc_cstate->input_buf_index)
					appendBinaryStringInfo(&fc_cstate->line_buf,
										   fc_cstate->input_buf + fc_cstate->input_buf_index,
										   fc_prev_raw_ptr - fc_cstate->input_buf_index);
				fc_cstate->input_buf_index = fc_input_buf_ptr;
				fc_result = true;	/* 报告EOF */
				break;
			}
			else if (!fc_cstate->opts.csv_mode)
			{
				/*
				 * 如果我们在这里，意味着我们找到了一个反斜杠后面跟着
				 * 除了句点以外的其他东西。在非CSV模式下，反斜杠后的
				 * 任何东西都是特殊的，因此我们也跳过那个第二个字符。
				 * 如果我们不这样做，\\将被视为复制的eof，而在非CSV模式下，
				 * 它是一个字面意义上的反斜杠后面跟一个句点。在CSV模式下，
				 * 反斜杠不是特殊的，因此我们希望像处理普通字符一样处理
				 * 反斜杠后的字符，因此在这些情况下我们不增加计数。
				 */
				fc_input_buf_ptr++;
			}
		}

		/*
		 * 这个标签是用于CSV情况，其中\.出现在行的开始，
		 * 但后面还有更多文本，这意味着它是一个数据值。
		 * 我们对CSV模式下的\.更加严格，因为\.可能是一个数据值，
		 * 而在非CSV模式下，\.不能是一个数据值。
		 */
not_end_of_copy:
		fc_first_char_in_line = false;
	}							/* 外循环结束 */

	/*
	 * 将任何仍未复制的数据传输到line_buf中。
	 */
	REFILL_LINEBUF;

	return fc_result;
}

/*
 *	返回十六进制数字的十进制值
 */
static int fc_GetDecimalFromHex(char fc_hex)
{
	if (isdigit((unsigned char) fc_hex))
		return fc_hex - '0';
	else
		return tolower((unsigned char) fc_hex) - 'a' + 10;
}

/*
 * 解析当前行并分离为独立属性（字段），
 * 根据需要进行反转义。
 *
 * 输入在 line_buf 中。我们使用 attribute_buf 来保存结果
 * 字符串。cstate->raw_fields[k] 被设置为指向第 k 个属性
 * 字符串，或者当输入匹配空标记字符串时为 NULL。
 * 该数组会根据需要扩展。
 *
 * （请注意，调用者无法检查 NULL，因为返回的
 * 字符串将是反转义后等价的，这可能与某些有效数据字符串
 * 看起来相同。）
 *
 * delim 是列分隔符字符串（现在必须是一个字节）。
 * null_print 是空标记字符串。请注意，这与
 * 反转义前的输入字符串进行比较。
 *
 * 返回值是实际读取的字段数量。
 */
static int fc_CopyReadAttributesText(CopyFromState fc_cstate)
{
	char		fc_delimc = fc_cstate->opts.delim[0];
	int			fc_fieldno;
	char	   *fc_output_ptr;
	char	   *fc_cur_ptr;
	char	   *fc_line_end_ptr;

	/*
	 * 我们需要为零列表格设置一个特殊情况：检查输入
	 * 行是否为空，并返回。
	 */
	if (fc_cstate->max_fields <= 0)
	{
		if (fc_cstate->line_buf.len != 0)
			ereport(ERROR,
					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
					 errmsg("extra data after last expected column")));
		return 0;
	}

	resetStringInfo(&fc_cstate->attribute_buf);

	/*
	 * 反转义的属性绝对不会比输入
	 * 数据行更长，因此我们可以强制 attribute_buf 足够大
	 * 然后在没有检查足够空间的情况下传输数据。我们需要
	 * 这样做，因为在流中间扩大 attribute_buf 会使已经存储
	 * 到 cstate->raw_fields[] 的指针无效。
	 */
	if (fc_cstate->attribute_buf.maxlen <= fc_cstate->line_buf.len)
		enlargeStringInfo(&fc_cstate->attribute_buf, fc_cstate->line_buf.len);
	fc_output_ptr = fc_cstate->attribute_buf.data;

	/* 设置循环的指针变量 */
	fc_cur_ptr = fc_cstate->line_buf.data;
	fc_line_end_ptr = fc_cstate->line_buf.data + fc_cstate->line_buf.len;

	/* 外层循环遍历字段 */
	fc_fieldno = 0;
	for (;;)
	{
		bool		fc_found_delim = false;
		char	   *fc_start_ptr;
		char	   *fc_end_ptr;
		int			fc_input_len;
		bool		fc_saw_non_ascii = false;

		/* 确保下一个值有足够的空间 */
		if (fc_fieldno >= fc_cstate->max_fields)
		{
			fc_cstate->max_fields *= 2;
			fc_cstate->raw_fields =
				repalloc(fc_cstate->raw_fields, fc_cstate->max_fields * sizeof(char *));
		}

		/* 记录输入和输出两侧字段的开始位置 */
		fc_start_ptr = fc_cur_ptr;
		fc_cstate->raw_fields[fc_fieldno] = fc_output_ptr;

		/*
		 * 扫描数据以查找字段。
		 *
		 * 请注意，在这个循环中，我们正在扫描以定位字段的结束
		 * 并且也在推测性地执行反转义。一旦我们找到字段的结束，
		 * 我们可以将原始字段内容与空标记字符串进行匹配。
		 * 只有在比较失败后，我们才知道反转义确实是正确的做法；
		 * 因此，我们 *绝对不* 能在完成空标记检查之前抛出任何语法错误。
		 */
		for (;;)
		{
			char		fc_c;

			fc_end_ptr = fc_cur_ptr;
			if (fc_cur_ptr >= fc_line_end_ptr)
				break;
			fc_c = *fc_cur_ptr++;
			if (fc_c == fc_delimc)
			{
				fc_found_delim = true;
				break;
			}
			if (fc_c == '\\')
			{
				if (fc_cur_ptr >= fc_line_end_ptr)
					break;
				fc_c = *fc_cur_ptr++;
				switch (fc_c)
				{
					case '0':
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
						{
							/* 处理 \013 */
							int			fc_val;

							fc_val = OCTVALUE(fc_c);
							if (fc_cur_ptr < fc_line_end_ptr)
							{
								fc_c = *fc_cur_ptr;
								if (ISOCTAL(fc_c))
								{
									fc_cur_ptr++;
									fc_val = (fc_val << 3) + OCTVALUE(fc_c);
									if (fc_cur_ptr < fc_line_end_ptr)
									{
										fc_c = *fc_cur_ptr;
										if (ISOCTAL(fc_c))
										{
											fc_cur_ptr++;
											fc_val = (fc_val << 3) + OCTVALUE(fc_c);
										}
									}
								}
							}
							fc_c = fc_val & 0377;
							if (fc_c == '\0' || IS_HIGHBIT_SET(fc_c))
								fc_saw_non_ascii = true;
						}
						break;
					case 'x':
						/* 处理 \x3F */
						if (fc_cur_ptr < fc_line_end_ptr)
						{
							char		fc_hexchar = *fc_cur_ptr;

							if (isxdigit((unsigned char) fc_hexchar))
							{
								int			fc_val = fc_GetDecimalFromHex(fc_hexchar);

								fc_cur_ptr++;
								if (fc_cur_ptr < fc_line_end_ptr)
								{
									fc_hexchar = *fc_cur_ptr;
									if (isxdigit((unsigned char) fc_hexchar))
									{
										fc_cur_ptr++;
										fc_val = (fc_val << 4) + fc_GetDecimalFromHex(fc_hexchar);
									}
								}
								fc_c = fc_val & 0xff;
								if (fc_c == '\0' || IS_HIGHBIT_SET(fc_c))
									fc_saw_non_ascii = true;
							}
						}
						break;
					case 'b':
						fc_c = '\b';
						break;
					case 'f':
						fc_c = '\f';
						break;
					case 'n':
						fc_c = '\n';
						break;
					case 'r':
						fc_c = '\r';
						break;
					case 't':
						fc_c = '\t';
						break;
					case 'v':
						fc_c = '\v';
						break;

						/*
						 * 在所有其他情况下，将 '\'
						 * 后的字符字面意义地处理
						 */
				}
			}

			/* 将 c 添加到输出字符串 */
			*fc_output_ptr++ = fc_c;
		}

		/* 检查原始输入是否匹配空标记 */
		fc_input_len = fc_end_ptr - fc_start_ptr;
		if (fc_input_len == fc_cstate->opts.null_print_len &&
			strncmp(fc_start_ptr, fc_cstate->opts.null_print, fc_input_len) == 0)
			fc_cstate->raw_fields[fc_fieldno] = NULL;
		else
		{
			/*
			 * 在这一点上，我们知道字段应该包含数据。
			 *
			 * 如果我们反转义了任何非 7 位 ASCII 字符，请确保
			 * 生成的字符串对于数据库编码是有效数据。
			 */
			if (fc_saw_non_ascii)
			{
				char	   *fc_fld = fc_cstate->raw_fields[fc_fieldno];

				pg_verifymbstr(fc_fld, fc_output_ptr - fc_fld, false);
			}
		}

		/* 结束输出区域中的属性值 */
		*fc_output_ptr++ = '\0';

		fc_fieldno++;
		/* 如果我们遇到行尾而不是分隔符则完成 */
		if (!fc_found_delim)
			break;
	}

	/* 清理 attribute_buf 的状态 */
	fc_output_ptr--;
	Assert(*fc_output_ptr == '\0');
	fc_cstate->attribute_buf.len = (fc_output_ptr - fc_cstate->attribute_buf.data);

	return fc_fieldno;
}

/*
 * 解析当前行并分离为独立属性（字段），
 * 根据需要进行反转义。它与 CopyReadAttributesText
 * 的 API 完全相同，只是我们根据
 * “标准”（即常见）CSV 用法解析字段。
 */
static int fc_CopyReadAttributesCSV(CopyFromState fc_cstate)
{
	char		fc_delimc = fc_cstate->opts.delim[0];
	char		fc_quotec = fc_cstate->opts.quote[0];
	char		fc_escapec = fc_cstate->opts.escape[0];
	int			fc_fieldno;
	char	   *fc_output_ptr;
	char	   *fc_cur_ptr;
	char	   *fc_line_end_ptr;

	/*
	 * 我们需要为零列表格设置一个特殊情况：检查输入
	 * 行是否为空，并返回。
	 */
	if (fc_cstate->max_fields <= 0)
	{
		if (fc_cstate->line_buf.len != 0)
			ereport(ERROR,
					(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
					 errmsg("extra data after last expected column")));
		return 0;
	}

	resetStringInfo(&fc_cstate->attribute_buf);

	/*
	 * 反转义的属性绝对不会比输入
	 * 数据行更长，因此我们可以强制 attribute_buf 足够大
	 * 然后在没有检查足够空间的情况下传输数据。我们需要
	 * 这样做，因为在流中间扩大 attribute_buf 会使已经存储
	 * 到 cstate->raw_fields[] 的指针无效。
	 */
	if (fc_cstate->attribute_buf.maxlen <= fc_cstate->line_buf.len)
		enlargeStringInfo(&fc_cstate->attribute_buf, fc_cstate->line_buf.len);
	fc_output_ptr = fc_cstate->attribute_buf.data;

	/* 设置循环的指针变量 */
	fc_cur_ptr = fc_cstate->line_buf.data;
	fc_line_end_ptr = fc_cstate->line_buf.data + fc_cstate->line_buf.len;

	/* 外层循环遍历字段 */
	fc_fieldno = 0;
	for (;;)
	{
		bool		fc_found_delim = false;
		bool		fc_saw_quote = false;
		char	   *fc_start_ptr;
		char	   *fc_end_ptr;
		int			fc_input_len;

		/* 确保下一个值有足够的空间 */
		if (fc_fieldno >= fc_cstate->max_fields)
		{
			fc_cstate->max_fields *= 2;
			fc_cstate->raw_fields =
				repalloc(fc_cstate->raw_fields, fc_cstate->max_fields * sizeof(char *));
		}

		/* 记录输入和输出两侧字段的开始位置 */
		fc_start_ptr = fc_cur_ptr;
		fc_cstate->raw_fields[fc_fieldno] = fc_output_ptr;

		/*
		 * 扫描数据以获取字段，
		 *
		 * 循环从“非引用”模式开始，然后在此模式和“引用”模式之间切换。如果处于“非引用”模式且看到分隔符或行结束，则循环正常退出。
		 */
		for (;;)
		{
			char		fc_c;

			/* 不在引用中 */
			for (;;)
			{
				fc_end_ptr = fc_cur_ptr;
				if (fc_cur_ptr >= fc_line_end_ptr)
					goto endfield;
				fc_c = *fc_cur_ptr++;
				/* 未引用的字段分隔符 */
				if (fc_c == fc_delimc)
				{
					fc_found_delim = true;
					goto endfield;
				}
				/* 引用字段（或字段部分）的开始 */
				if (fc_c == fc_quotec)
				{
					fc_saw_quote = true;
					break;
				}
				/* 将 c 添加到输出字符串 */
				*fc_output_ptr++ = fc_c;
			}

			/* 在引用中 */
			for (;;)
			{
				fc_end_ptr = fc_cur_ptr;
				if (fc_cur_ptr >= fc_line_end_ptr)
					ereport(ERROR,
							(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
							 errmsg("unterminated CSV quoted field")));

				fc_c = *fc_cur_ptr++;

				/* 引用字段中的转义 */
				if (fc_c == fc_escapec)
				{
					/*
					 * 如果下一个字符可用，则窥探它，如果它是转义字符或引用字符，则进行转义
					 */
					if (fc_cur_ptr < fc_line_end_ptr)
					{
						char		fc_nextc = *fc_cur_ptr;

						if (fc_nextc == fc_escapec || fc_nextc == fc_quotec)
						{
							*fc_output_ptr++ = fc_nextc;
							fc_cur_ptr++;
							continue;
						}
					}
				}

				/*
				 * 引用字段的结束。必须在测试转义之后进行此测试，以防引用字符和转义字符相同
				 * （这是常见情况）。
				 */
				if (fc_c == fc_quotec)
					break;

				/* 将 c 添加到输出字符串 */
				*fc_output_ptr++ = fc_c;
			}
		}
endfield:

		/* 结束输出区域中的属性值 */
		*fc_output_ptr++ = '\0';

		/* 检查原始输入是否匹配空标记 */
		fc_input_len = fc_end_ptr - fc_start_ptr;
		if (!fc_saw_quote && fc_input_len == fc_cstate->opts.null_print_len &&
			strncmp(fc_start_ptr, fc_cstate->opts.null_print, fc_input_len) == 0)
			fc_cstate->raw_fields[fc_fieldno] = NULL;

		fc_fieldno++;
		/* 如果我们遇到行尾而不是分隔符则完成 */
		if (!fc_found_delim)
			break;
	}

	/* 清理 attribute_buf 的状态 */
	fc_output_ptr--;
	Assert(*fc_output_ptr == '\0');
	fc_cstate->attribute_buf.len = (fc_output_ptr - fc_cstate->attribute_buf.data);

	return fc_fieldno;
}


/*
 * 读取二进制属性
 */
static Datum fc_CopyReadBinaryAttribute(CopyFromState fc_cstate, FmgrInfo *fc_flinfo,
						Oid fc_typioparam, int32 fc_typmod,
						bool *fc_isnull)
{
	int32		fc_fld_size;
	Datum		fc_result;

	if (!fc_CopyGetInt32(fc_cstate, &fc_fld_size))
		ereport(ERROR,
				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
				 errmsg("unexpected EOF in COPY data")));
	if (fc_fld_size == -1)
	{
		*fc_isnull = true;
		return ReceiveFunctionCall(fc_flinfo, NULL, fc_typioparam, fc_typmod);
	}
	if (fc_fld_size < 0)
		ereport(ERROR,
				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
				 errmsg("invalid field size")));

	/* 将 attribute_buf 重置为空，并加载原始数据 */
	resetStringInfo(&fc_cstate->attribute_buf);

	enlargeStringInfo(&fc_cstate->attribute_buf, fc_fld_size);
	if (fc_CopyReadBinaryData(fc_cstate, fc_cstate->attribute_buf.data,
						   fc_fld_size) != fc_fld_size)
		ereport(ERROR,
				(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
				 errmsg("unexpected EOF in COPY data")));

	fc_cstate->attribute_buf.len = fc_fld_size;
	fc_cstate->attribute_buf.data[fc_fld_size] = '\0';

	/* 调用列类型的二进制输入转换器 */
	fc_result = ReceiveFunctionCall(fc_flinfo, &fc_cstate->attribute_buf,
								 fc_typioparam, fc_typmod);

	/* 如果没有读取完整个缓冲区则会出问题 */
	if (fc_cstate->attribute_buf.cursor != fc_cstate->attribute_buf.len)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
				 errmsg("incorrect binary data format")));

	*fc_isnull = false;
	return fc_result;
}
